package com.young.ums.util;

import com.young.common.util.*;
import com.young.ums.model.User;
import com.young.ums.model.SsoJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Base64;
import java.util.Calendar;
import java.util.Date;


/**
 * ums安全相关处理类
 * Created by imrookie on 2018/9/15.
 */
public class SecureHandler {

    private static Logger logger = LoggerFactory.getLogger(SecureHandler.class);

    /**
     * 加密
     * 用于用户密码加密等
     * @param str 待加密的明文
     * @return 返回加密后的密文
     */
    public static String encryptPwd(String str){
        return MD5Util.MD5(MD5Util.MD5(str));
    }

    /**
     * 生成jwt对象
     * @param user 用户信息
     * @param validTime 令牌有效时间，单位：秒
     * @param hs256Key 签名私钥
     * @return jwt对象
     */
    public static SsoJWT generateJWT(User user, int validTime, String hs256Key) throws Exception {
        SsoJWT jwt = SsoJWT.newInstance();//初始化jwt
        //设置头
        jwt.getHeader().setTyp("JWT");
        jwt.getHeader().setAlg("HS256");

        //设置荷载
        jwt.getPayload().setIss("young");
        jwt.getPayload().setSub(user.getId());
        jwt.getPayload().setAud(user.getId());
        Date iat = new Date();
        jwt.getPayload().setIat(iat);//当前时间作为签发时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(iat);
        calendar.add(Calendar.SECOND, validTime);
        jwt.getPayload().setExp(calendar.getTime());//过期时间
        jwt.getPayload().setUserId(user.getId());//用户id
        jwt.getPayload().setUuid(CommonUtil.getUUID());//设置唯一编号

        //签名
        String headerStr = Base64.getEncoder().encodeToString(CommonJsonUtil.beanToJsonStr(jwt.getHeader()).getBytes());//经过base64编码后的头部JSONObject.fromObject(jwt.getHeader()).toString()
        String payloadStr = Base64.getEncoder().encodeToString(CommonJsonUtil.beanToJsonStr(jwt.getPayload()).getBytes());//经过base64编码后的头部JSONObject.fromObject(jwt.getPayload()).toString()
        String msg = headerStr + "." + payloadStr;
        try {
            jwt.setSignature(HS256Util.encrypt(msg, hs256Key));//hs256加密算法进行签名
        } catch (Exception e) {
            logger.error("[生成JWT] HS256算法签名失败，明文信息：{}，密钥：{}", msg, hs256Key, e);
            throw e;
        }

        return jwt;
    }

    /**
     * 签发jwt
     * 1.按照header.payload.签名拼接好jwt字符串
     * 2.对jwt字符串使用RSA加密(防止敏感信息流出)
     * @param jwt 装配完成的JWT对象
     * @param publicKey token进行RSA加密使用的公钥
     * @return 下发给客户端的经过加密的jwt认证信息
     */
    public static String signJWT(SsoJWT jwt, String publicKey) throws Exception {
        String headerStr = Base64.getEncoder().encodeToString(CommonJsonUtil.beanToJsonStr(jwt.getHeader()).getBytes());//经过base64编码后的头部JSONObject.fromObject(jwt.getHeader()).toString()
        String payloadStr = Base64.getEncoder().encodeToString(CommonJsonUtil.beanToJsonStr(jwt.getPayload()).getBytes());//经过base64编码后的头部JSONObject.fromObject(jwt.getPayload()).toString()
        String token = headerStr + "." + payloadStr + "." + jwt.getSignature();//jwt规范令牌

        //对jwt令牌加密返回
        String secretToken;

        try {
            secretToken = Base64.getEncoder().encodeToString(RSAUtil.encryptByPublic(token.getBytes(), publicKey));
        } catch (Exception e) {
            logger.error("[生成JWT] RSA算法公钥加密失败，JWT信息：{}，公钥：{}", token, publicKey, e);
            throw e;
        }
        return secretToken;
    }

    /**
     * 解析jwt串
     * @param jwt jwt字符串
     * @param privateKey RSA私钥
     * @param hs256Key 签名私钥,hs256秘钥,用于重新计算签名,防止数据篡改
     * @return 解析成功的jwt对象
     */
    public static SsoJWT parseJWT(String jwt, String privateKey, String hs256Key) throws Exception {
        SsoJWT ssoJWT = null;
        try {
            String jwt2 = new String(RSAUtil.decryptByPrivate(Base64.getDecoder().decode(jwt), privateKey));//解密token
            String[] jwtItems = jwt2.split("\\.");
            if (jwtItems != null && jwtItems.length == 3){
                String header = jwtItems[0];//头部
                String payload = jwtItems[1];//荷载
                String sign = jwtItems[2];//签名
                //重新计算签名
                String signNew = HS256Util.encrypt(header+"."+payload, hs256Key);
                if (!signNew.equals(sign)){//重新签名与token中的签名部分不一致,说明数据可能被篡改
                    logger.error("[解析JWT串] 根据JWT串重新计算签名与原始签名不一致,数据可能已经被篡改!解密后的JWT串:{},原始签名:{},重新计算签名:{}",jwt2 , sign, signNew);
                    throw new IllegalArgumentException("jwt串签名异常");
                }
                //签名验证完毕,证明数据安全,接下来就是解析为jwt对象
                ssoJWT = SsoJWT.newInstance(CommonJsonUtil.jsonStrToBean(new String(Base64.getDecoder().decode(jwtItems[0])), SsoJWT.Header.class),
                        CommonJsonUtil.jsonStrToBean(new String(Base64.getDecoder().decode(jwtItems[1])), SsoJWT.Payload.class),
                        signNew);
                //判断个部分是否为空,为空抛出异常,保证从该方法出去的jwt对象是有效的登录令牌
                if (ssoJWT.getHeader() == null || ssoJWT.getPayload() == null || StringUtils.isBlank(ssoJWT.getSignature())){
                    logger.error("[解析JWT串] jwt串解析失败(可能是json转obj出错)，解密后的jwt为:{}", jwt2);
                    throw new IllegalArgumentException("jwt串解析失败");
                }
            }else{
                logger.error("[解析JWT串] jwt串格式有误，解密后的jwt为:{}", jwt2);
                throw new IllegalArgumentException("jwt串格式错误");
            }
        } catch (Exception e) {
            logger.error("[解析JWT串] RSA算法私钥解密失败，JWT串：{}", jwt, e);
            throw e;
        }
        return ssoJWT;
    }

}
